package com.sudo.api.aspect;

import com.sudo.common.annotation.ApiIdempotent;
import com.sudo.common.component.RedisKeys;
import com.sudo.common.component.StringRedisUtil;
import com.sudo.common.exception.BizException;
import com.sudo.common.utils.*;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Order(1)
@Slf4j
@Aspect
@Component
public class ApiIdempotentAspect {

    @Resource
    public StringRedisUtil stringRedisUtil;
    @Resource
    private RedisKeys redisKeys;
    /**
     * 实现 redis 的 get delete 原子性 的lua 脚本
     */
    final private static String RELEASE_LOCK_LUA_SCRIPT =
            " if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "  return redis.call('del', KEYS[1]) " +
            " else " +
            "  return 0 " +
            " end";

    @Pointcut("@annotation(com.sudo.common.annotation.ApiIdempotent)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取 使用注解的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        ApiIdempotent apiIdempotent = method.getDeclaredAnnotation(ApiIdempotent.class);
        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法名
        String methodName = joinPoint.getSignature().getName();
        HttpServletRequest request = HttpServletUtil.getRequest();
        String username = HttpUserInfoUtil.getSessionUser(request);
        if (StringUtils.isBlank(username)) {
            username = IpAddressUtil.getIpAddress(request);
            username += MD5Util.getMD5Str(UserAgent.parseUserAgentString(request.getHeader("user-agent")).toString());
        }
        String lockKey = redisKeys.getRedisApiIdempotentKey(username, className, methodName);
        String randomKey = RandomUtil.getUUID();

        try {
            boolean setIfAbsent = stringRedisUtil.setIfAbsent(lockKey, randomKey, apiIdempotent.expire(), apiIdempotent.timeUnit());
            if (setIfAbsent) {
                // 放行
                return joinPoint.proceed();
            }
            throw new BizException("请勿重复提交");
        }finally {
            stringRedisUtil.execute(RELEASE_LOCK_LUA_SCRIPT, lockKey, randomKey);
        }

    }

}
